Imports System
Imports System.Runtime.Serialization
Imports System.Globalization
Imports System.ComponentModel
Imports System.Diagnostics

Namespace Gravitybox.Common.Serialization

  Public Class Serialize

    Private Const NullExceptionString As String = "The type cannot be null!"

#Region "Constructor"

    Private Sub New()
    End Sub

#End Region

#Region "ConvertToString"

    Public Shared Function ConvertToString(ByVal value As Object, ByVal useCurrentCulture As Boolean) As String
      Return ConvertToString(value, Nothing, useCurrentCulture)
    End Function

    Public Shared Function ConvertToString(ByVal value As Object, ByVal format As String, ByVal useCurrentCulture As Boolean) As String
      Dim culture As CultureInfo = IIf(useCurrentCulture, CultureInfo.CurrentCulture, CultureInfo.InvariantCulture)
      Return ConvertToString(value, format, culture)
    End Function

    Public Shared Function ConvertToString(ByVal value As Object, ByVal formatProvider As IFormatProvider) As String
      Return ConvertToString(value, Nothing, formatProvider)
    End Function

    Public Shared Function ConvertToString(ByVal value As Object, ByVal format As String, ByVal formatProvider As IFormatProvider) As String

      If (value Is Nothing) Then
        Return Nothing
      End If

      If (formatProvider Is Nothing) Then
        formatProvider = CultureInfo.CurrentCulture
      End If

      If (value Is GetType(IFormattable)) Then
        Return (CType(value, IFormattable)).ToString(format, formatProvider)
      End If

      If (Not (formatProvider Is GetType(CultureInfo))) Then
        formatProvider = CultureInfo.CurrentCulture
      End If

      Return CType(ConvertValue(value, GetType(String), CType(formatProvider, CultureInfo)), String)

    End Function

#End Region

#Region "ConvertValue"

    Public Shared Function ConvertValue(ByVal value As Object, ByVal destinationType As Type) As Object
      Return ConvertValue(value, destinationType, False)
    End Function

    Public Shared Function ConvertValue(ByVal value As Object, ByVal destinationType As Type, ByVal useCurrentCulture As Boolean) As Object
      Dim culture As CultureInfo = IIf(useCurrentCulture, CultureInfo.CurrentCulture, CultureInfo.InvariantCulture)
      Return ConvertValue(value, destinationType, culture)
    End Function

    Public Shared Function ConvertValue(ByVal value As Object, ByVal destinationType As Type, ByVal culture As CultureInfo) As Object

      If (destinationType Is Nothing) Then
        Throw New Exception(NullExceptionString)
      End If

      If (value Is Nothing) Then
        Return Nothing
      End If

      Dim ValueType As Type = value.GetType

      If (ValueType Is destinationType OrElse ValueType.IsSubclassOf(destinationType)) Then
        Return value
      End If

      If (culture Is Nothing) Then
        culture = CultureInfo.CurrentCulture
      End If

      If (value Is GetType(IConvertible) AndAlso _
         (destinationType.IsPrimitive OrElse _
         destinationType Is GetType(String) OrElse _
         destinationType Is GetType(DateTime))) Then

        Return Convert.ChangeType(value, destinationType, culture)
      End If

      Dim converter As TypeConverter = TypeDescriptor.GetConverter(destinationType)
      If (Not converter.CanConvertFrom(ValueType)) Then
        Dim sourceTypeConverter As TypeConverter = TypeDescriptor.GetConverter(ValueType)

        If (sourceTypeConverter.CanConvertTo(destinationType)) Then
          Return sourceTypeConverter.ConvertTo(Nothing, culture, value, destinationType)
        End If

        Return value
      End If

      Return converter.ConvertFrom(Nothing, culture, value)

    End Function

#End Region

#Region "ConvertEnum"

    Public Shared Function ConvertEnum(ByVal value As Object, ByVal defaultValue As [Enum]) As [Enum]

      If (value Is Nothing) Then
        Return defaultValue
      End If

      Dim ValueType As Type = value.GetType()
      Dim enumType As Type = defaultValue.GetType()

      If (ValueType.IsEnum AndAlso Not (ValueType Is enumType)) Then
        Return defaultValue
      End If

      Dim converted As Object

      If (ValueType Is enumType) Then
        converted = value
      Else
        Throw New Exception("Non-convertable type!")
      End If

      If ([Enum].IsDefined(enumType, converted)) Then
        Return [Enum].ToObject(enumType, converted)
      End If

      Dim attribs() As Object = enumType.GetCustomAttributes(GetType(FlagsAttribute), False)

      If (attribs.Length > 0 AndAlso attribs(0) Is GetType(FlagsAttribute)) Then
        Return [Enum].ToObject(enumType, converted)
      End If

      Return defaultValue

    End Function

#End Region

#Region "IsSimpleType"

    Public Shared Function IsSimpleType(ByVal value As Object) As Boolean

      If (value Is Nothing) Then
        Return False
      End If

      Return IsSimpleType(value.GetType())

    End Function

    Public Shared Function IsSimpleType(ByVal type As System.Type) As Boolean

      If (type Is GetType(String) OrElse _
       type Is GetType(Decimal) OrElse _
       type Is GetType(DateTime) OrElse _
       type Is GetType(System.DBNull)) Then
        Return True
      End If

      If (type.IsPrimitive) Then
        Return True
      End If

      Return False

    End Function

#End Region

#Region "IsSafelySerializable"

    Public Shared Function IsSafelySerializable(ByVal value As Object) As Boolean

      If (value Is Nothing) Then
        Return False
      End If

      Return IsSafelySerializable(value.GetType())

    End Function

    Public Shared Function IsSafelySerializable(ByVal type As Type) As Boolean

      If (type Is Nothing) Then
        Return False
      End If

      If (type.IsSerializable) Then
        Return True
      End If

      If (IsSimpleType(type)) Then
        Return True
      End If

      If (type Is GetType(System.Drawing.Size) OrElse _
       type Is GetType(System.Drawing.Rectangle) OrElse _
       type Is GetType(System.Drawing.Point)) Then
        Return True
      End If

      If (type Is GetType(System.Drawing.Icon) OrElse _
       type Is GetType(System.Drawing.Image) OrElse _
       type.IsSubclassOf(GetType(System.Drawing.Image)) OrElse _
       type.IsSubclassOf(GetType(System.Drawing.Icon))) Then
        Return True
      End If

      Return False

    End Function

#End Region

#Region "SerializeProperty"

    Public Shared Sub SerializeProperty(ByVal info As SerializationInfo, ByVal name As String, ByVal value As Object)

      If (info Is Nothing) Then
        Throw New Exception(NullExceptionString)
      End If

      If (value Is Nothing) Then
        Return
      End If

      If (value Is GetType(System.Enum)) Then
        Dim underlyingType As Type = [Enum].GetUnderlyingType(value.GetType())
        info.AddValue(name, CType(value, IConvertible).ToType(underlyingType, CultureInfo.InvariantCulture))
      Else
        info.AddValue(name, value)
      End If

    End Sub

#End Region

#Region "DeserializeProperty"

    Public Shared Function DeserializeProperty(ByVal entry As SerializationEntry, ByVal destinationType As Type, ByVal defaultValue As Object) As Object
      Dim succeeded As Boolean
      Return Gravitybox.Common.Serialization.Serialize.DeserializeProperty(entry, destinationType, defaultValue, succeeded)
    End Function

    Public Shared Function DeserializeProperty(ByVal entry As SerializationEntry, ByVal destinationType As Type, ByVal defaultValue As Object, ByRef succeeded As Boolean) As Object

      Dim deserializedValue As Object = defaultValue

      If (destinationType Is Nothing) Then
        Throw New Exception(NullExceptionString)
      End If

      If (defaultValue Is Nothing) Then
        If (destinationType.IsValueType) Then
          Throw New ArgumentException("")
        Else
          If (Not (defaultValue.GetType() Is destinationType) AndAlso _
           Not defaultValue.GetType().IsSubclassOf(destinationType)) Then
            Throw New ArgumentException("")
          End If
        End If

        Try
          If (destinationType.IsEnum) Then
            deserializedValue = Gravitybox.Common.Serialization.Serialize.ConvertEnum(entry.Value, CType(defaultValue, System.Enum))
          Else
            deserializedValue = Gravitybox.Common.Serialization.Serialize.ConvertValue(entry.Value, destinationType)
            succeeded = True
          End If
        Catch
          Debug.Fail("De-serialization failed")
          succeeded = False
        End Try

        Return deserializedValue

      End If

    End Function

#End Region

  End Class

end namespace

